Exemplo n.º 1
0
        public void InterruptDeviceResetWorksAsExpected()
        {
            // --- Arrange
            var vm   = new SpectrumAdvancedTestMachine();
            var idev = new InterruptDevice(TEST_INT_TACT);

            idev.OnAttachedToVm(vm);

            // --- Act
            idev.Reset();

            // --- Assert
            idev.InterruptRaised.ShouldBeFalse();
            idev.InterruptRevoked.ShouldBeFalse();
        }
Exemplo n.º 2
0
        public void InterruptIsRaisedWithinAllowedRange()
        {
            for (var tact = TEST_INT_TACT; tact <= TEST_INT_TACT + InterruptDevice.LONGEST_OP_TACTS; tact++)
            {
                // --- Arrange
                var vm   = new SpectrumAdvancedTestMachine();
                var idev = new InterruptDevice(TEST_INT_TACT);
                idev.OnAttachedToVm(vm);

                // --- Act/Assert
                idev.CheckForInterrupt(tact);
                idev.InterruptRaised.ShouldBeTrue();
                idev.InterruptRevoked.ShouldBeFalse();
                idev.FrameCount.ShouldBe(1);
                (vm.Cpu.StateFlags & Z80StateFlags.Int).ShouldBe(Z80StateFlags.Int);
            }
        }
Exemplo n.º 3
0
        public void InterruptNotRaisedTooEarly()
        {
            // --- Arrange
            var vm   = new SpectrumAdvancedTestMachine();
            var idev = new InterruptDevice(TEST_INT_TACT);

            idev.OnAttachedToVm(vm);

            // --- Act/Assert
            for (var tact = 0; tact < TEST_INT_TACT; tact++)
            {
                idev.CheckForInterrupt(tact);
                idev.InterruptRaised.ShouldBeFalse();
                idev.InterruptRevoked.ShouldBeFalse();
                idev.FrameCount.ShouldBe(0);
                (vm.Cpu.StateFlags & Z80StateFlags.Int).ShouldBe(Z80StateFlags.None);
            }
        }
Exemplo n.º 4
0
        public void InterruptIsNotRereaised()
        {
            // --- Arrange
            var vm   = new SpectrumAdvancedTestMachine();
            var idev = new InterruptDevice(TEST_INT_TACT);

            idev.OnAttachedToVm(vm);
            idev.CheckForInterrupt(TEST_INT_TACT);

            // --- Act
            vm.Cpu.StateFlags &= Z80StateFlags.InvInt;
            idev.CheckForInterrupt(TEST_INT_TACT + 1);

            // --- Assert
            idev.InterruptRaised.ShouldBeTrue();
            idev.InterruptRevoked.ShouldBeFalse();
            (vm.Cpu.StateFlags & Z80StateFlags.Int).ShouldBe(Z80StateFlags.None);
            idev.FrameCount.ShouldBe(1);
        }
Exemplo n.º 5
0
        public void InterruptIsNotRaisedTooLate()
        {
            // --- Arrange
            var vm   = new SpectrumAdvancedTestMachine();
            var idev = new InterruptDevice(TEST_INT_TACT);

            idev.OnAttachedToVm(vm);

            // --- Act/Assert
            var lateTact = TEST_INT_TACT + InterruptDevice.LONGEST_OP_TACTS + 1;

            for (var tact = lateTact; tact < lateTact + 10; tact++)
            {
                idev.CheckForInterrupt(tact);
                idev.InterruptRaised.ShouldBeFalse();
                idev.InterruptRevoked.ShouldBeTrue();
                idev.FrameCount.ShouldBe(0);
                (vm.Cpu.StateFlags & Z80StateFlags.Int).ShouldBe(Z80StateFlags.None);
            }
        }
Exemplo n.º 6
0
        /// <summary>
        /// The main execution cycle of the Spectrum VM
        /// </summary>
        /// <param name="token">Cancellation token</param>
        /// <param name="options">Execution options</param>
        /// <return>True, if the cycle completed; false, if it has been cancelled</return>
        public bool ExecuteCycle(CancellationToken token, ExecuteCycleOptions options)
        {
            ExecuteCycleOptions          = options;
            ExecutionCompletionReason    = ExecutionCompletionReason.None;
            LastExecutionStartTact       = Cpu.Tacts;
            LastExecutionContentionValue = ContentionAccumulated;

            // --- We use these variables to calculate wait time at the end of the frame
            var cycleStartTime  = Clock.GetCounter();
            var cycleStartTact  = Cpu.Tacts;
            var cycleFrameCount = 0;

            // --- We use this variable to check whether to stop in Debug mode
            var executedInstructionCount = -1;

            // --- Loop #1: The main cycle that goes on until cancelled
            while (!token.IsCancellationRequested)
            {
                if (_frameCompleted)
                {
                    // --- This counter helps us to calculate where we are in the frame after
                    // --- each CPU operation cycle
                    LastFrameStartCpuTick = Cpu.Tacts - Overflow;

                    // --- Notify devices to start a new frame
                    OnNewFrame();
                    LastRenderedUlaTact = Overflow;
                    _frameCompleted     = false;
                }

                // --- Loop #2: The physical frame cycle that goes on while CPU and ULA
                // --- processes everything whithin a physical frame (0.019968 second)
                while (!_frameCompleted)
                {
                    // --- Check for leaving maskable interrupt mode
                    if (RunsInMaskableInterrupt)
                    {
                        if (Cpu.Registers.PC == 0x0052)
                        {
                            // --- We leave the maskable interrupt mode when the
                            // --- current instruction completes
                            RunsInMaskableInterrupt = false;
                        }
                    }

                    // --- Check debug mode when a CPU instruction has been entirelly executed
                    if (!Cpu.IsInOpExecution)
                    {
                        // --- Check for cancellation
                        if (token.IsCancellationRequested)
                        {
                            ExecutionCompletionReason = ExecutionCompletionReason.Cancelled;
                            return(false);
                        }

                        // --- The next instruction is about to be executed
                        executedInstructionCount++;

                        // --- Check for timeout
                        if (options.TimeoutTacts > 0 &&
                            cycleStartTact + options.TimeoutTacts < Cpu.Tacts)
                        {
                            ExecutionCompletionReason = ExecutionCompletionReason.Timeout;
                            return(false);
                        }

                        // --- Check for reaching the termination point
                        if (options.EmulationMode == EmulationMode.UntilExecutionPoint)
                        {
                            if (options.TerminationPoint < 0x4000)
                            {
                                // --- ROM & address must match
                                if (options.TerminationRom == MemoryDevice.GetSelectedRomIndex() &&
                                    options.TerminationPoint == Cpu.Registers.PC)
                                {
                                    // --- We reached the termination point within ROM
                                    ExecutionCompletionReason = ExecutionCompletionReason.TerminationPointReached;
                                    return(true);
                                }
                            }
                            else if (options.TerminationPoint == Cpu.Registers.PC)
                            {
                                // --- We reached the termination point within RAM
                                ExecutionCompletionReason = ExecutionCompletionReason.TerminationPointReached;
                                return(true);
                            }
                        }

                        // --- Check for entering maskable interrupt mode
                        if (Cpu.MaskableInterruptModeEntered)
                        {
                            RunsInMaskableInterrupt = true;
                        }

                        // --- Check for a debugging stop point
                        if (options.EmulationMode == EmulationMode.Debugger)
                        {
                            if (IsDebugStop(options, executedInstructionCount))
                            {
                                // --- At this point, the cycle should be stopped because of debugging reasons
                                // --- The screen should be refreshed
                                ScreenDevice.OnFrameCompleted();
                                ExecutionCompletionReason = ExecutionCompletionReason.BreakpointReached;
                                return(true);
                            }
                        }
                    }

                    // --- Check for interrupt signal generation
                    InterruptDevice.CheckForInterrupt(CurrentFrameTact);

                    // --- Run a single Z80 instruction
                    Cpu.ExecuteCpuCycle();
                    _lastBreakpoint = null;

                    // --- Run a rendering cycle according to the current CPU tact count
                    var lastTact = CurrentFrameTact;
                    ScreenDevice.RenderScreen(LastRenderedUlaTact + 1, lastTact);
                    LastRenderedUlaTact = lastTact;

                    // --- Exit if the emulation mode specifies so
                    if (options.EmulationMode == EmulationMode.UntilHalt &&
                        (Cpu.StateFlags & Z80StateFlags.Halted) != 0)
                    {
                        ExecutionCompletionReason = ExecutionCompletionReason.Halted;
                        return(true);
                    }

                    // --- Notify each CPU-bound device that the current operation has been completed
                    foreach (var device in _cpuBoundDevices)
                    {
                        device.OnCpuOperationCompleted();
                    }

                    // --- Decide whether this frame has been completed
                    _frameCompleted = !Cpu.IsInOpExecution && CurrentFrameTact >= _frameTacts;
                } // -- End Loop #2

                // --- A physical frame has just been completed. Take care about screen refresh
                cycleFrameCount++;
                FrameCount++;

                // --- Notify devices that the current frame completed
                OnFrameCompleted();

                // --- Exit if the emulation mode specifies so
                if (options.EmulationMode == EmulationMode.UntilFrameEnds)
                {
                    ExecutionCompletionReason = ExecutionCompletionReason.FrameCompleted;
                    return(true);
                }

                // --- Wait while the frame time ellapses
                if (!ExecuteCycleOptions.FastVmMode)
                {
                    var nextFrameCounter = cycleStartTime + cycleFrameCount * PhysicalFrameClockCount;
                    Clock.WaitUntil((long)nextFrameCounter, token);
                }

                // --- Start a new frame and carry on
                Overflow = CurrentFrameTact % _frameTacts;
            } // --- End Loop #1

            // --- The cycle has been interrupted by cancellation
            ExecutionCompletionReason = ExecutionCompletionReason.Cancelled;
            return(false);
        }
Exemplo n.º 7
0
        /// <summary>
        /// Initializes a class instance using a collection of devices
        /// </summary>
        public SpectrumEngine(DeviceInfoCollection deviceData)
        {
            DeviceData = deviceData ?? throw new ArgumentNullException(nameof(deviceData));

            // --- Check for Spectrum Next
            var nextInfo = GetDeviceInfo <INextFeatureSetDevice>();

            NextDevice = nextInfo?.Device;

            // --- Prepare the memory device
            var memoryInfo = GetDeviceInfo <IMemoryDevice>();

            MemoryDevice        = memoryInfo?.Device ?? new Spectrum48MemoryDevice();
            MemoryConfiguration = (IMemoryConfiguration)memoryInfo?.ConfigurationData;

            // --- Prepare the port device
            var portInfo = GetDeviceInfo <IPortDevice>();

            PortDevice = portInfo?.Device ?? new Spectrum48PortDevice();

            // --- Init the CPU
            var cpuConfig = GetDeviceConfiguration <IZ80Cpu, ICpuConfiguration>();
            var mult      = 1;

            if (cpuConfig != null)
            {
                BaseClockFrequency = cpuConfig.BaseClockFrequency;
                mult = cpuConfig.ClockMultiplier;
                if (mult < 1)
                {
                    mult = 1;
                }
                else if (mult >= 2 && mult <= 3)
                {
                    mult = 2;
                }
                else if (mult >= 4 && mult <= 7)
                {
                    mult = 4;
                }
                else if (mult > 8)
                {
                    mult = 8;
                }
            }
            ClockMultiplier = mult;
            Cpu             = new Z80Cpu(MemoryDevice,
                                         PortDevice,
                                         cpuConfig?.SupportsNextOperations ?? false,
                                         NextDevice)
            {
                UseGateArrayContention = MemoryConfiguration.ContentionType == MemoryContentionType.GateArray
            };

            // --- Init the ROM
            var romInfo = GetDeviceInfo <IRomDevice>();

            RomProvider      = (IRomProvider)romInfo.Provider;
            RomDevice        = romInfo.Device ?? new SpectrumRomDevice();
            RomConfiguration = (IRomConfiguration)romInfo.ConfigurationData;

            // --- Init the clock
            var clockInfo = GetDeviceInfo <IClockDevice>();

            Clock = (IClockProvider)clockInfo.Provider
                    ?? throw new InvalidOperationException("The virtual machine needs a clock provider!");

            // --- Init the screen device
            var screenInfo    = GetDeviceInfo <IScreenDevice>();
            var pixelRenderer = (IScreenFrameProvider)screenInfo.Provider;

            ScreenConfiguration = new ScreenConfiguration((IScreenConfiguration)screenInfo.ConfigurationData);
            ScreenDevice        = screenInfo.Device ?? new Spectrum48ScreenDevice();

            // --- Init the beeper device
            var beeperInfo = GetDeviceInfo <IBeeperDevice>();

            AudioConfiguration = (IAudioConfiguration)beeperInfo?.ConfigurationData;
            BeeperProvider     = (IBeeperProvider)beeperInfo?.Provider;
            BeeperDevice       = beeperInfo?.Device ?? new BeeperDevice();

            // --- Init the keyboard device
            var keyboardInfo = GetDeviceInfo <IKeyboardDevice>();

            KeyboardProvider = (IKeyboardProvider)keyboardInfo?.Provider;
            KeyboardDevice   = keyboardInfo?.Device ?? new KeyboardDevice();

            // --- Init the interrupt device
            InterruptDevice = new InterruptDevice(InterruptTact);

            // --- Init the tape device
            var tapeInfo = GetDeviceInfo <ITapeDevice>();

            TapeProvider = (ITapeProvider)tapeInfo?.Provider;
            TapeDevice   = tapeInfo?.Device
                           ?? new TapeDevice(TapeProvider);

            // === Init optional devices
            // --- Init the sound device
            var soundInfo = GetDeviceInfo <ISoundDevice>();

            SoundConfiguration = (IAudioConfiguration)soundInfo?.ConfigurationData;
            SoundProvider      = (ISoundProvider)soundInfo?.Provider;
            SoundDevice        = soundInfo == null
                ? null
                : soundInfo.Device ?? new SoundDevice();

            // --- Init the DivIDE device
            var divIdeInfo = GetDeviceInfo <IDivIdeDevice>();

            DivIdeDevice = divIdeInfo?.Device;

            // --- Carry out frame calculations
            ResetUlaTact();
            _frameTacts             = ScreenConfiguration.ScreenRenderingFrameTactCount;
            PhysicalFrameClockCount = Clock.GetFrequency() / (double)BaseClockFrequency * _frameTacts;
            FrameCount              = 0;
            Overflow                = 0;
            _frameCompleted         = true;
            _lastBreakpoint         = null;
            RunsInMaskableInterrupt = false;

            // --- Attach providers
            AttachProvider(RomProvider);
            AttachProvider(Clock);
            AttachProvider(pixelRenderer);
            AttachProvider(BeeperProvider);
            AttachProvider(KeyboardProvider);
            AttachProvider(TapeProvider);
            AttachProvider(DebugInfoProvider);

            // --- Attach optional providers
            if (SoundProvider != null)
            {
                AttachProvider(SoundProvider);
            }

            // --- Collect Spectrum devices
            _spectrumDevices.Add(RomDevice);
            _spectrumDevices.Add(MemoryDevice);
            _spectrumDevices.Add(PortDevice);
            _spectrumDevices.Add(ScreenDevice);
            _spectrumDevices.Add(BeeperDevice);
            _spectrumDevices.Add(KeyboardDevice);
            _spectrumDevices.Add(InterruptDevice);
            _spectrumDevices.Add(TapeDevice);

            // --- Collect optional devices
            if (SoundDevice != null)
            {
                _spectrumDevices.Add(SoundDevice);
            }
            if (NextDevice != null)
            {
                _spectrumDevices.Add(NextDevice);
            }
            if (DivIdeDevice != null)
            {
                _spectrumDevices.Add(DivIdeDevice);
            }

            // --- Now, prepare devices to find each other
            foreach (var device in _spectrumDevices)
            {
                device.OnAttachedToVm(this);
            }

            // --- Prepare bound devices
            _frameBoundDevices = _spectrumDevices
                                 .OfType <IFrameBoundDevice>()
                                 .ToList();
            _cpuBoundDevices = _spectrumDevices
                               .OfType <ICpuOperationBoundDevice>()
                               .ToList();

            DebugInfoProvider = new SpectrumDebugInfoProvider();

            // --- Init the ROM
            InitRom(RomDevice, RomConfiguration);
        }
Exemplo n.º 8
0
        /// <summary>
        /// The main execution cycle of the Spectrum VM
        /// </summary>
        /// <param name="token">Cancellation token</param>
        /// <param name="options">Execution options</param>
        /// <param name="completeOnCpuFrame">The cycle should complete on CPU frame completion</param>
        /// <return>True, if the cycle completed; false, if it has been cancelled</return>
        public bool ExecuteCycle(CancellationToken token, ExecuteCycleOptions options, bool completeOnCpuFrame = false)
        {
            ExecuteCycleOptions       = options;
            ExecutionCompletionReason = ExecutionCompletionReason.None;
            LastExecutionStartTact    = Cpu.Tacts;

            // --- We use this variables to check whether to stop in Debug mode
            var executedInstructionCount = -1;
            var entryStepOutDepth        = Cpu.StackDebugSupport.StepOutStackDepth;

            // --- Check if we're just start running the next frame
            if (HasFrameCompleted)
            {
                // --- This counter helps us to calculate where we are in the frame after
                // --- each CPU operation cycle
                LastFrameStartCpuTick = Cpu.Tacts - Overflow;

                // --- Notify devices to start a new frame
                OnNewFrame();
                LastRenderedUlaTact = Overflow;
                HasFrameCompleted   = false;
            }

            // --- The physical frame cycle that goes on while CPU and ULA
            // --- processes everything within a physical frame (0.019968 second)
            while (!HasFrameCompleted)
            {
                // --- Check debug mode when a CPU instruction has been entirely executed
                if (!Cpu.IsInOpExecution)
                {
                    // --- The next instruction is about to be executed
                    executedInstructionCount++;

                    // --- Check for cancellation
                    if (token.IsCancellationRequested)
                    {
                        ExecutionCompletionReason = ExecutionCompletionReason.Cancelled;
                        return(false);
                    }

                    // --- Check for CPU frame completion
                    if (completeOnCpuFrame && Cpu.Tacts > LastExecutionStartTact + CPU_FRAME)
                    {
                        ExecutionCompletionReason = ExecutionCompletionReason.CpuFrameCompleted;
                        return(true);
                    }

                    // --- Check for several termination modes
                    switch (options.EmulationMode)
                    {
                    // --- Check for reaching the termination point
                    case EmulationMode.UntilExecutionPoint when options.TerminationPoint < 0x4000:
                    {
                        // --- ROM & address must match
                        if (options.TerminationRom == MemoryDevice.GetSelectedRomIndex() &&
                            options.TerminationPoint == Cpu.Registers.PC)
                        {
                            // --- We reached the termination point within ROM
                            ExecutionCompletionReason = ExecutionCompletionReason.TerminationPointReached;
                            return(true);
                        }

                        break;
                    }

                    // --- Check if we reached the termination point within RAM
                    case EmulationMode.UntilExecutionPoint
                        when options.TerminationPoint == Cpu.Registers.PC:

                        ExecutionCompletionReason = ExecutionCompletionReason.TerminationPointReached;
                        return(true);

                    // --- Check for a debugging stop point
                    case EmulationMode.Debugger
                        when IsDebugStop(options, executedInstructionCount, entryStepOutDepth) &&
                        DebugConditionSatisfied():

                        // --- At this point, the cycle should be stopped because of debugging reasons
                        // --- The screen should be refreshed
                        ExecutionCompletionReason = ExecutionCompletionReason.BreakpointReached;

                        return(true);
                    }
                }

                // --- Check for interrupt signal generation
                InterruptDevice.CheckForInterrupt(CurrentFrameTact);

                // --- Run a single Z80 instruction
                Cpu.ExecuteCpuCycle();
                _lastBreakpoint = null;

                // --- Run a rendering cycle according to the current CPU tact count
                var lastTact = CurrentFrameTact;
                ScreenDevice.RenderScreen(LastRenderedUlaTact + 1, lastTact);
                LastRenderedUlaTact = lastTact;

                // --- Exit if the CPU is HALTed and the emulation mode specifies so
                if (options.EmulationMode == EmulationMode.UntilHalt &&
                    (Cpu.StateFlags & Z80StateFlags.Halted) != 0)
                {
                    ExecutionCompletionReason = ExecutionCompletionReason.Halted;
                    return(true);
                }

                // --- Notify each CPU-bound device that the current operation has been completed
                foreach (var device in _cpuBoundDevices)
                {
                    device.OnCpuOperationCompleted();
                }

                // --- Decide whether this frame has been completed
                HasFrameCompleted = !Cpu.IsInOpExecution && CurrentFrameTact >= FrameTacts;
            }

            // --- A physical frame has just been completed. Take care about screen refresh
            FrameCount++;
            Overflow = CurrentFrameTact % FrameTacts;

            // --- Notify devices that the current frame completed
            OnFrameCompleted();

            // --- We exit the cycle when the render frame has completed
            ExecutionCompletionReason = ExecutionCompletionReason.RenderFrameCompleted;
            return(true);
        }
Exemplo n.º 9
0
        /// <summary>
        /// Initializes a class instance using a collection of devices
        /// </summary>
        public SpectrumEngine(DeviceInfoCollection deviceData, string ulaIssue = "3")
        {
            DeviceData = deviceData ?? throw new ArgumentNullException(nameof(deviceData));
            UlaIssue   = ulaIssue == "3" ? "3" : "2";

            // --- Prepare the memory device
            var memoryInfo = GetDeviceInfo <IMemoryDevice>();

            MemoryDevice        = memoryInfo?.Device ?? new Spectrum48MemoryDevice();
            MemoryConfiguration = (IMemoryConfiguration)memoryInfo?.ConfigurationData;

            // --- Prepare the port device
            var portInfo = GetDeviceInfo <IPortDevice>();

            PortDevice = portInfo?.Device ?? new Spectrum48PortDevice();

            // --- Init the CPU
            var cpuConfig = GetDeviceConfiguration <IZ80Cpu, ICpuConfiguration>();
            var mult      = 1;

            if (cpuConfig != null)
            {
                BaseClockFrequency = cpuConfig.BaseClockFrequency;
                mult = cpuConfig.ClockMultiplier;
                if (mult < 1)
                {
                    mult = 1;
                }
                else if (mult >= 2 && mult <= 3)
                {
                    mult = 2;
                }
                else if (mult >= 4 && mult <= 7)
                {
                    mult = 4;
                }
                else if (mult > 8)
                {
                    mult = 8;
                }
            }
            ClockMultiplier = mult;
            Cpu             = new Z80Cpu(MemoryDevice,
                                         PortDevice,
                                         cpuConfig?.SupportsNextOperations ?? false)
            {
                UseGateArrayContention = MemoryConfiguration.ContentionType == MemoryContentionType.GateArray
            };

            // --- Init the ROM
            var romInfo = GetDeviceInfo <IRomDevice>();

            RomProvider      = (IRomProvider)romInfo.Provider;
            RomDevice        = romInfo.Device ?? new SpectrumRomDevice();
            RomConfiguration = (IRomConfiguration)romInfo.ConfigurationData;

            // --- Init the screen device
            var screenInfo    = GetDeviceInfo <IScreenDevice>();
            var pixelRenderer = (IScreenFrameProvider)screenInfo.Provider;

            ScreenConfiguration = new ScreenConfiguration((IScreenConfiguration)screenInfo.ConfigurationData);
            ScreenDevice        = screenInfo.Device ?? new Spectrum48ScreenDevice();
            ShadowScreenDevice  = new Spectrum48ScreenDevice();

            // --- Init the beeper device
            var beeperInfo = GetDeviceInfo <IBeeperDevice>();

            AudioConfiguration = (IAudioConfiguration)beeperInfo?.ConfigurationData;
            BeeperProvider     = (IBeeperProvider)beeperInfo?.Provider;
            BeeperDevice       = beeperInfo?.Device ?? new BeeperDevice();

            // --- Init the keyboard device
            var keyboardInfo = GetDeviceInfo <IKeyboardDevice>();

            KeyboardProvider = (IKeyboardProvider)keyboardInfo?.Provider;
            KeyboardDevice   = keyboardInfo?.Device ?? new KeyboardDevice();

            // --- Init the Kempston device
            var kempstonInfo = GetDeviceInfo <IKempstonDevice>();

            KempstonProvider = (IKempstonProvider)kempstonInfo?.Provider;
            KempstonDevice   = kempstonInfo?.Device ?? new KempstonDevice();

            // --- Init the interrupt device
            InterruptDevice = new InterruptDevice(InterruptTact);

            // --- Init the tape device
            var tapeSaveInfo = GetDeviceInfo <ITapeSaveDevice>();

            TapeSaveProvider = (ITapeSaveProvider)tapeSaveInfo?.Provider;
            var tapeLoadInfo = GetDeviceInfo <ITapeLoadDevice>();

            TapeLoadProvider = (ITapeLoadProvider)tapeLoadInfo?.Provider;
            var tapeDevice = new TapeDevice(TapeLoadProvider, TapeSaveProvider);

            TapeLoadDevice = tapeDevice;
            TapeSaveDevice = tapeDevice;

            // === Init optional devices
            // --- Init the sound device
            var soundInfo = GetDeviceInfo <ISoundDevice>();

            SoundConfiguration = (IAudioConfiguration)soundInfo?.ConfigurationData;
            SoundProvider      = (ISoundProvider)soundInfo?.Provider;
            SoundDevice        = soundInfo == null
                ? null
                : soundInfo.Device ?? new SoundDevice();

            // --- Init the floppy device
            var floppyInfo = GetDeviceInfo <IFloppyDevice>();

            if (floppyInfo != null)
            {
                FloppyDevice        = floppyInfo.Device;
                FloppyConfiguration = (IFloppyConfiguration)floppyInfo.ConfigurationData ?? new FloppyConfiguration();
            }

            // --- Carry out frame calculations
            ResetUlaTact();
            FrameTacts        = ScreenConfiguration.ScreenRenderingFrameTactCount;
            FrameCount        = 0;
            Overflow          = 0;
            HasFrameCompleted = true;
            _lastBreakpoint   = null;

            // --- Attach providers
            AttachProvider(RomProvider);
            AttachProvider(pixelRenderer);
            AttachProvider(BeeperProvider);
            AttachProvider(KeyboardProvider);
            AttachProvider(KempstonProvider);
            AttachProvider(TapeLoadProvider);
            AttachProvider(DebugInfoProvider);

            // --- Attach optional providers
            if (SoundProvider != null)
            {
                AttachProvider(SoundProvider);
            }

            // --- Collect Spectrum devices
            _spectrumDevices.Add(RomDevice);
            _spectrumDevices.Add(MemoryDevice);
            _spectrumDevices.Add(PortDevice);
            _spectrumDevices.Add(ScreenDevice);
            _spectrumDevices.Add(ShadowScreenDevice);
            _spectrumDevices.Add(BeeperDevice);
            _spectrumDevices.Add(KeyboardDevice);
            _spectrumDevices.Add(KempstonDevice);
            _spectrumDevices.Add(InterruptDevice);
            _spectrumDevices.Add(TapeLoadDevice);

            // --- Collect optional devices
            if (SoundDevice != null)
            {
                _spectrumDevices.Add(SoundDevice);
            }
            if (FloppyDevice != null)
            {
                _spectrumDevices.Add(FloppyDevice);
            }

            // --- Now, prepare devices to find each other
            foreach (var device in _spectrumDevices)
            {
                device.OnAttachedToVm(this);
            }

            // --- Prepare bound devices
            _frameBoundDevices = _spectrumDevices
                                 .OfType <IRenderFrameBoundDevice>()
                                 .ToList();
            _cpuBoundDevices = _spectrumDevices
                               .OfType <ICpuOperationBoundDevice>()
                               .ToList();

            DebugInfoProvider = new SpectrumDebugInfoProvider();

            // --- Init the ROM
            InitRom(RomDevice, RomConfiguration);
        }
Exemplo n.º 10
0
        /// <summary>
        /// The main execution cycle of the Spectrum VM
        /// </summary>
        /// <param name="token">Cancellation token</param>
        /// <param name="options">Execution options</param>
        /// <return>True, if the cycle completed; false, if it has been cancelled</return>
        public bool ExecuteCycle(CancellationToken token, ExecuteCycleOptions options)
        {
            ExecuteCycleOptions = options;

            // --- We use these variables to calculate wait time at the end of the frame
            var cycleStartTime  = Clock.GetCounter();
            var cycleFrameCount = 0;

            // --- We use this variable to check whether to stop in Debug mode
            var executedInstructionCount = -1;

            // --- Loop #1: The main cycle that goes on until cancelled
            while (!token.IsCancellationRequested)
            {
                if (_frameCompleted)
                {
                    // --- This counter helps us to calculate where we are in the frame after
                    // --- each CPU operation cycle
                    LastFrameStartCpuTick = Cpu.Tacts - Overflow;

                    // --- Notify devices to start a new frame
                    OnNewFrame();
                    LastRenderedUlaTact = Overflow;
                    _frameCompleted     = false;
                }

                // --- We use this timestamp for debug information calculation
                var currentFrameStartCounter = Clock.GetCounter();

                DebugInfoProvider.CpuTime             = 0;
                DebugInfoProvider.ScreenRenderingTime = 0;

                // --- Loop #2: The physical frame cycle that goes on while CPU and ULA
                // --- processes everything whithin a physical frame (0.019968 second)
                while (!_frameCompleted)
                {
                    // --- Check debug mode when a CPU instruction has been entirelly executed
                    if (!Cpu.IsInOpExecution)
                    {
                        // --- Check for cancellation
                        if (token.IsCancellationRequested)
                        {
                            return(false);
                        }

                        // --- The next instruction is about to be executed
                        executedInstructionCount++;

                        // --- Check for reaching the termination point
                        if (options.EmulationMode == EmulationMode.UntilExecutionPoint &&
                            options.TerminationPoint == Cpu.Registers.PC)
                        {
                            // --- We reached the termination point
                            return(true);
                        }

                        // --- Check for a debugging stop point
                        if (options.EmulationMode == EmulationMode.Debugger)
                        {
                            if (IsDebugStop(options.DebugStepMode, executedInstructionCount))
                            {
                                // --- At this point, the cycle should be stopped because of debugging reasons
                                // --- The screen should be refreshed
                                ScreenDevice.OnFrameCompleted();
                                return(true);
                            }
                        }
                    }

                    // --- Check for interrupt signal generation
                    InterruptDevice.CheckForInterrupt(CurrentFrameTact);

                    // --- Run a single Z80 instruction
                    var cpuStart = Clock.GetCounter();
                    Cpu.ExecuteCpuCycle();
                    DebugInfoProvider.CpuTime += Clock.GetCounter() - cpuStart;

                    // --- Run a rendering cycle according to the current CPU tact count
                    var lastTact    = CurrentFrameTact;
                    var renderStart = Clock.GetCounter();
                    ScreenDevice.RenderScreen(LastRenderedUlaTact + 1, lastTact);
                    DebugInfoProvider.ScreenRenderingTime += Clock.GetCounter() - renderStart;
                    LastRenderedUlaTact = lastTact;

                    // --- Exit if the emulation mode specifies so
                    if (options.EmulationMode == EmulationMode.UntilHalt &&
                        (Cpu.StateFlags & Z80StateFlags.Halted) != 0)
                    {
                        return(true);
                    }

                    // --- Notify each CPU-bound device that the current operation has been completed
                    foreach (var device in _cpuBoundDevices)
                    {
                        device.OnCpuOperationCompleted();
                    }

                    // --- Decide whether this frame has been completed
                    _frameCompleted = !Cpu.IsInOpExecution && CurrentFrameTact >= _frameTacts;
                } // -- End Loop #2

                // --- A physical frame has just been completed. Take care about screen refresh
                cycleFrameCount++;
                FrameCount++;

                // --- Calculate debug information
                DebugInfoProvider.FrameTime   = Clock.GetCounter() - currentFrameStartCounter;
                DebugInfoProvider.UtilityTime = DebugInfoProvider.FrameTime - DebugInfoProvider.CpuTime
                                                - DebugInfoProvider.ScreenRenderingTime;
                DebugInfoProvider.CpuTimeInMs             = DebugInfoProvider.CpuTime / (double)Clock.GetFrequency() * 1000;
                DebugInfoProvider.ScreenRenderingTimeInMs =
                    DebugInfoProvider.ScreenRenderingTime / (double)Clock.GetFrequency() * 1000;
                DebugInfoProvider.UtilityTimeInMs =
                    DebugInfoProvider.UtilityTime / (double)Clock.GetFrequency() * 1000;
                DebugInfoProvider.FrameTimeInMs = DebugInfoProvider.FrameTime / (double)Clock.GetFrequency() * 1000;

                // --- Notify devices that the current frame completed
                OnFrameCompleted();

                // --- Exit if the emulation mode specifies so
                if (options.EmulationMode == EmulationMode.UntilFrameEnds)
                {
                    return(true);
                }

                // --- Wait while the frame time ellapses
                var nextFrameCounter = cycleStartTime + cycleFrameCount * PhysicalFrameClockCount;
                Clock.WaitUntil((long)nextFrameCounter, token);

                // --- Start a new frame and carry on
                Overflow = CurrentFrameTact % _frameTacts;
            } // --- End Loop #1

            // --- The cycle has been inerrupted by cancellation
            return(false);
        }
Exemplo n.º 11
0
        /// <summary>Initializes a new instance of the <see cref="T:System.Object" /> class.</summary>
        public Spectrum48(
            IRomProvider romProvider,
            IClockProvider clockProvider,
            IKeyboardProvider keyboardProvider,
            IScreenFrameProvider pixelRenderer,
            IEarBitFrameProvider earBitFrameProvider   = null,
            ITapeContentProvider loadContentProvider   = null,
            ISaveToTapeProvider tapeSaveToTapeProvider = null)
        {
            // --- Init the CPU
            MemoryDevice  = new Spectrum48MemoryDevice();
            PortDevice    = new Spectrum48PortDevice();
            Cpu           = new Z80Cpu(MemoryDevice, PortDevice);
            OsInitialized = false;

            // --- Setup the clock
            Clock = clockProvider;

            // --- Set up Spectrum devices
            BorderDevice    = new BorderDevice();
            ScreenDevice    = new Spectrum48ScreenDevice(pixelRenderer);
            BeeperDevice    = new BeeperDevice(earBitFrameProvider);
            KeyboardDevice  = new KeyboardDevice(keyboardProvider);
            InterruptDevice = new InterruptDevice(InterruptTact);
            TapeDevice      = new TapeDevice(loadContentProvider, tapeSaveToTapeProvider);

            // --- Carry out frame calculations

            ResetUlaTact();
            _frameTacts             = ScreenDevice.ScreenConfiguration.UlaFrameTactCount;
            PhysicalFrameClockCount = Clock.GetFrequency() / (double)ClockFrequeny * _frameTacts;
            FrameCount      = 0;
            Overflow        = 0;
            _frameCompleted = true;

            // --- Collect Spectrum devices
            _spectrumDevices.Add(MemoryDevice);
            _spectrumDevices.Add(PortDevice);
            _spectrumDevices.Add(BorderDevice);
            _spectrumDevices.Add(ScreenDevice);
            _spectrumDevices.Add(BeeperDevice);
            _spectrumDevices.Add(KeyboardDevice);
            _spectrumDevices.Add(InterruptDevice);
            _spectrumDevices.Add(TapeDevice);

            // --- Now, prepare devices to find each other
            foreach (var device in _spectrumDevices)
            {
                device.OnAttachedToVm(this);
            }

            // --- Prepare bound devices
            _frameBoundDevices = _spectrumDevices
                                 .OfType <IFrameBoundDevice>()
                                 .ToList();
            _cpuBoundDevices = _spectrumDevices
                               .OfType <ICpuOperationBoundDevice>()
                               .ToList();

            DebugInfoProvider = new SpectrumDebugInfoProvider();

            // --- Init the ROM
            InitRom(romProvider, "ZXSpectrum48");
        }